package com.uxsino.watcher.lib.configuration;

import com.alibaba.fastjson.JSONObject;
import com.uxsino.commons.baseclass.Constants;
import com.uxsino.commons.baseclass.ICreateMenu;
import com.uxsino.commons.cache.Cache;
import com.uxsino.commons.lic.AppProperty;
import com.uxsino.reactorq.commons.JMSFlux;
import com.uxsino.reactorq.commons.ReactorQFactory;
import com.uxsino.reactorq.constant.EventTopicConstants;
import com.uxsino.reactorq.event.Event;
import com.uxsino.reactorq.event.EventSource;
import com.uxsino.reactorq.event.IEvent;
import com.uxsino.reactorq.event.ModuleOnLineEvent;
import com.uxsino.reactorq.subscriber.EventSubscriber;
import com.uxsino.watcher.lib.interceptor.ComponentFilter;
import com.uxsino.watcher.lib.interceptor.LicInterceptor;
import com.uxsino.watcher.lib.services.LicChanged;
import com.uxsino.watcher.lib.services.VersionParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import reactor.core.publisher.EmitterProcessor;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Configuration
public class SimoComponentConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {
    private final Logger LOG = LoggerFactory.getLogger(SimoComponentConfig.class);


    @Value(value = "${spring.application.name:#{null}}")
    private String app;

    @Autowired
    @Qualifier("default_reactorq_factory")
    private ReactorQFactory rqFactory;

    @Autowired
    @Qualifier("outbox_components")
    private EventSubscriber _outbox;

    @Autowired
    LicInterceptor licInterceptor;

    private EmitterProcessor<com.uxsino.reactorq.event.Event> outboxProcessor = EmitterProcessor.<Event> create();

    ApplicationContext applicationContext;

    @Autowired(required = false)
    Set<LicChanged> ies;

    @Autowired(required = false)
    Set<ICreateMenu> menus;

    @Autowired
    VersionParser parser;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private static Object LOCKER = new byte[0];

    @PostConstruct
    public void init() throws Exception{
        rqFactory.createTopicFlux(EventTopicConstants.SIMO_LICENSE_NOTIFY, AppProperty.class).subscribe(JMSFlux.Catch((property)->{
            synchronized (LOCKER){
                try {
                    if(property.getClean()){
                        Cache.remove(Constants.LIC_INFO);
                        LOG.warn("[license-dispatcher] warn: clean");
                    }else{
                        LOG.info("[license-receive]: {}", property);
                        Cache.cache(Constants.LIC_INFO, (itm)->{
                            itm.setDatas(property);
                            itm.setExpired(null);
                        });
                    }
                }catch (Exception e){
                    LOG.warn("[license-receive] error: {}", e);
                }
                if(ies != null){
                    ies.forEach((i)->{
                        try {
                            i.accept();
                        }catch (Exception e){
                            LOG.warn("[license-dispatcher] error: 执行变更: {} 发生异常：{}", i.getClass().getSimpleName(), e);
                        }
                    });
                }
            }
        }, LOG));
        EventSource inbox = new EventSource(rqFactory);
        inbox.subscribeEvent(ModuleOnLineEvent.class, "", this::online);
        rqFactory.createTopicFlux("PROJECT_VERSION_INFO", VersionParser.Info.class).subscribe(JMSFlux.Catch((v)->{
            VersionParser.INFO.put(v.getName(), v);
        }));
        initMenus();
        sendInfo();
    }

    private void online(IEvent evt){
        ModuleOnLineEvent ev = (ModuleOnLineEvent) evt;
        if ("mc".equals(ev.Module)) {
            initMenus();
            sendInfo();
        }
    }

    private void sendInfo(){
        VersionParser.Info info = parser.show();
        try {
            _outbox.sendObjectOnTopic("PROJECT_VERSION_INFO", info);
        }catch (Exception e){
            LOG.error("[info-notiry-err]: {}", e);
        }
    }

    private void initMenus(){
        if(menus != null && !menus.isEmpty()){
            menus.forEach(m->{
                try {
                    JSONObject menu = m.create();
                    if(menu != null){
                        _outbox.sendObjectOnTopic(EventTopicConstants.SIMO_UPDATE_MENU, menu);
                    }
                }catch (Exception e){
                    LOG.error("[menu-init-notify-err]: {}", e);
                }
            });
        }
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(licInterceptor).excludePathPatterns(LicInterceptor.EXCLUDE_PATTERN);
    }

    @Bean(name = "outbox_components")
    //@Scope("singleton")
    public EventSubscriber outBox() {
        EventSubscriber outboxEventSubscriber = new EventSubscriber(rqFactory);
        outboxEventSubscriber.setFrom("components");
        outboxProcessor.subscribe(outboxEventSubscriber);
        return outboxEventSubscriber;
    }

    @Bean
    public FilterRegistrationBean<ComponentFilter> filter() {
        ComponentFilter filter = new ComponentFilter(app);
        FilterRegistrationBean<ComponentFilter> registrationBean = new FilterRegistrationBean<ComponentFilter>();
        registrationBean.setFilter(filter);
        List<String> urlPatterns = new ArrayList<String>();
        urlPatterns.add("/state");// 拦截路径，可以添加多个
        registrationBean.setUrlPatterns(urlPatterns);
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registrationBean;
    }
}
